package encrypter

import (
	"encoding/json"
	"fmt"
	"gitee.com/hilaoyu/go-basic-utils/utilHttp"
	"github.com/patrickmn/go-cache"
	"io"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

type ApiReturnJson struct {
	Status  bool        `json:"status"`
	Code    int         `json:"code"`
	Message string      `json:"message"`
	Errors  []string    `json:"errors"`
	Data    interface{} `json:"data"`
}

var dataIdCache = cache.New(time.Duration(5)*time.Minute, time.Duration(1)*time.Minute)

type ApiDataEncrypter struct {
	Key                     string
	AppId                   string
	Timeout                 time.Duration
	SslVerify               bool
	SslClientCertPemPath    string
	SslClientCertPemContent []byte
	SslClientCertPemKey     []byte

	utilHttp *utilHttp.UtilHttp
}

func (ae *ApiDataEncrypter) SetTimeout(timeout time.Duration) *ApiDataEncrypter {
	ae.Timeout = timeout
	return ae
}
func (ae *ApiDataEncrypter) buildUtilHttp() *utilHttp.UtilHttp {
	if nil == ae.utilHttp {
		uh := utilHttp.New("", ae.Timeout)
		uh.SetSslVerify(ae.SslVerify)
		if nil != ae.SslClientCertPemContent {
			uh.SetClientCertPemContent(ae.SslClientCertPemContent, ae.SslClientCertPemKey)
		}
		if "" != ae.SslClientCertPemPath {
			uh.SetClientCertPemPath(ae.SslClientCertPemPath)
		}
		ae.utilHttp = uh
	}
	return ae.utilHttp
}

func (ae *ApiDataEncrypter) Encrypt(data interface{}) (string, error) {
	return Encrypt(ae.Key, data)
}
func (ae *ApiDataEncrypter) EncryptString(data string) (string, error) {
	return EncryptString(ae.Key, data)
}

func (ae *ApiDataEncrypter) DecryptRaw(data string, v interface{}) error {
	return Decrypt(ae.Key, data, v)
}
func (ae *ApiDataEncrypter) DecryptString(data string) (string, error) {
	return DecryptString(ae.Key, data)
}
func (ae *ApiDataEncrypter) Decrypt(data string, v interface{}, debug ...bool) error {
	apiData := struct {
		DataId       string `json:"_data_id"`
		Timestamp    int64  `json:"_timestamp"`
		TimestampOld int64  `json:"timestamp"`
	}{}
	err := ae.DecryptRaw(data, &apiData)
	if nil != err {
		return err
	}

	now := time.Now().UTC()

	if len(debug) <= 0 || !debug[0] {
		t := apiData.Timestamp
		if t <= 0 {
			t = apiData.TimestampOld
		}
		if t <= now.Add(time.Duration(-3)*time.Minute).Unix() || t > now.Add(time.Duration(3)*time.Minute).Unix() {
			return fmt.Errorf("数据已过期")
		}
	}
	if len(debug) <= 1 || !debug[1] {
		if "" == apiData.DataId {
			return fmt.Errorf("数据错误")
		}

		if _, ok := dataIdCache.Get(apiData.DataId); ok {
			return fmt.Errorf("数据已处理过了")
		}
		dataIdCache.SetDefault(apiData.DataId, true)
	}

	if nil == v {
		return nil
	}
	return ae.DecryptRaw(data, v)
}

func (ae *ApiDataEncrypter) BuildEnParams(params url.Values, data map[string]interface{}) (url.Values, error) {
	return ae.buildEnParams(params, data)
}
func (ae *ApiDataEncrypter) buildEnParams(params url.Values, data map[string]interface{}) (url.Values, error) {

	data["_timestamp"] = time.Now().UTC().Unix()
	data["_data_id"] = strconv.FormatInt(time.Now().UTC().UnixNano(), 10)
	enData, err := ae.Encrypt(data)
	if nil != err {
		return nil, err
	}
	params.Set("data", enData)
	if "" != ae.AppId {
		params.Set("app_id", ae.AppId)
	}
	//godump.Dump(params)
	return params, nil
}

func (ae *ApiDataEncrypter) RequestJson(v interface{}, method string, remoteUrl string, data map[string]interface{}, params url.Values, headers map[string]string, timeout ...time.Duration) error {

	if len(timeout) > 0 {
		ae.SetTimeout(timeout[0])
	}

	params, err := ae.buildEnParams(params, data)

	if nil != err {
		return err
	}
	return ae.buildUtilHttp().RequestJson(v, method, remoteUrl, params, headers)
}
func (ae *ApiDataEncrypter) RequestJsonDecryptString(method string, remoteUrl string, data map[string]interface{}, params url.Values, headers map[string]string, timeout ...time.Duration) (resStr string, err error) {

	result := ApiReturnJson{}
	err = ae.RequestJson(&result, method, remoteUrl, data, params, headers, timeout...)

	if nil != err {
		return
	}

	if enStr, ok := result.Data.(string); ok {
		return ae.DecryptString(enStr)
	}
	return
}
func (ae *ApiDataEncrypter) RequestJsonDecryptData(v interface{}, method string, remoteUrl string, data map[string]interface{}, params url.Values, headers map[string]string, timeout ...time.Duration) (err error) {

	result := ApiReturnJson{}
	err = ae.RequestJson(&result, method, remoteUrl, data, params, headers, timeout...)

	if nil != err {
		return err
	}

	if nil == v {
		return nil
	}

	if enStr, ok := result.Data.(string); ok {
		return ae.DecryptRaw(enStr, v)
	}
	return
}

func (ae *ApiDataEncrypter) Request(method string, remoteUrl string, data map[string]interface{}, params url.Values, headers map[string]string, timeout ...time.Duration) (*http.Response, error) {

	if len(timeout) > 0 {
		ae.SetTimeout(timeout[0])
	}

	params, err := ae.buildEnParams(params, data)
	if nil != err {
		return nil, err
	}

	return ae.buildUtilHttp().Request(method, remoteUrl, params, headers)
}

func (ae *ApiDataEncrypter) SignRequestJson(v interface{}, method string, remoteUrl string, params url.Values, headers map[string]string, timeout ...time.Duration) error {
	resp, err := ae.SignRequest(method, remoteUrl, params, headers, timeout...)

	if err != nil {
		return err
	}
	//godump.Dump(resp)
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(body, &v)
	if err != nil {
		return err
	}
	return nil
}

func (ae *ApiDataEncrypter) SignRequest(method string, remoteUrl string, params url.Values, headers map[string]string, timeout ...time.Duration) (resp *http.Response, err error) {
	params.Del("sign")
	params.Set("_timestamp", strconv.FormatInt(time.Now().Unix(), 10))
	params.Set("_data_id", strconv.FormatInt(time.Now().UnixNano(), 10))
	sign := Md5(params.Encode() + ae.Key)
	params.Set("sign", sign)
	//params.Set("_ap", config.GetApiAdminPassword())

	uh := utilHttp.New("", timeout...)

	return uh.Request(method, remoteUrl, params, map[string]string{
		"Content-Type": "application/x-www-form-urlencoded",
		"Connection":   "close",
	})
}
